@file:JvmName("ReflectUtils")
/*
 * Copyright (C) 2016 Lokiy(liulongke@gmail.com)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *  　　　　http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */
package com.lokiy.kit.utils

import java.lang.reflect.Field
import java.lang.reflect.Method
import java.lang.reflect.ParameterizedType
import java.lang.reflect.Type
import java.util.*

/**
 * is normal GenericType(include Long,long,String,Double,double...)
 *
 * @param type type
 * @return is normal generic type
 */
fun Type.isNormalGenericType(): Boolean {
    return this === Long::class.java || this === Long::class.javaPrimitiveType || this === String::class.java || this === Double::class.java ||
            this === Double::class.javaPrimitiveType || this === Int::class.java || this === Int::class.javaPrimitiveType ||
            this === Float::class.java || this === Float::class.javaPrimitiveType || this === Short::class.java ||
            this === Short::class.javaPrimitiveType || this === Array<Byte>::class.java || this === ByteArray::class.java ||
            this === Boolean::class.javaPrimitiveType || this === Boolean::class.java
}

/**
 *
 * @param field field
 * @param <T> t
 * @return class
</T> */
fun <T> Field.getFieldClass(): Class<T>? {
    val type2 = this.type
    val type = this.genericType
    var clazz: Class<T>? = null
    if ((type2 == MutableList::class.java || type2 == ArrayList::class.java) && type is ParameterizedType) {
        clazz = type.actualTypeArguments[0] as Class<T>
    } else if (type is Class<*>) {
        clazz = type as Class<T>
    }
    return clazz
}
//
///**
// * @param item item
// * @param method method
// * @param parameterTypes parameterTypes
// * @param args args
// * @return method value
// *
// * @throws Exception exception
// */
//@Throws(Exception::class)
//fun Any.methodValue(method: String, parameterTypes: Array<Class<*>?>, vararg args: Any?): Any? =
//    javaClass.method(method, *parameterTypes).invoke(this, *args)


///**
// * @param filedName
// */
//operator fun <T> Any.get(filedName: String) = javaClass.field(filedName)[this] as? T


///**
// *
// * @param filedName
// */
//operator fun Any.set(filedName: String, value: Any?) {
//    javaClass.field(filedName)[this] = value
//}

/**
 *
 * @param name field name
 * @return field
 */
fun Class<*>.field(name: String): Field {
    val field: Field? = try {
        getDeclaredField(name)
    } catch (e: Exception) {
        superclass?.field(name)
    }
    requireNotNull(field) { "can't find field $name in Class $this" }
    field.isAccessible = true
    return field
}

/**
 * @param name method
 * @param parameterTypes parameterTypes
 * @return method value
 *
 * @throws Exception exception
 */
fun Class<*>.method(name: String, vararg parameterTypes: Class<*>?): Method {
    val m: Method? = try {
        getDeclaredMethod(name, *parameterTypes)
    } catch (ignored: Exception) {
        superclass?.method(name, *parameterTypes)
    }
    requireNotNull(m) { "can't find method $name in Class $this" }
    m.isAccessible = true
    return m
}
